package com.springboot.cloud.demos.feign.rest;

import cn.jmessage.api.common.model.RegisterInfo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.extension.api.R;
import com.springboot.cloud.common.core.entity.vo.Result;
import com.springboot.cloud.common.core.exception.SystemErrorType;
import com.springboot.cloud.common.core.jiguang.JiGuangUtil;
import com.springboot.cloud.common.core.qcloud.QcloudUtil;
import com.springboot.cloud.common.core.sensitive.SensitiveFilter;
import com.springboot.cloud.common.core.sms.SmsUtil;
import com.springboot.cloud.common.core.util.DateUtils;
import com.springboot.cloud.common.core.util.RandomUtil;
import com.springboot.cloud.common.core.util.RsaEncrypt;
import com.springboot.cloud.common.web.entity.constant.WeixinConstant;
import com.springboot.cloud.demos.entity.enums.VipStatus;
import com.springboot.cloud.demos.entity.form.*;
import com.springboot.cloud.demos.entity.param.FollowQueryParam;
import com.springboot.cloud.demos.entity.po.Moment;
import com.springboot.cloud.demos.entity.po.User;
import com.springboot.cloud.demos.entity.po.UserPosition;
import com.springboot.cloud.demos.feign.service.*;
import com.springboot.cloud.demos.feign.weixin.AES;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

@Slf4j
@RestController
public class UserController extends BaseController {

    @Autowired
    private UserService userService;
    @Autowired
    private UserPositionService userPositionService;
    @Autowired
    private MomentService momentService;
    @Autowired
    private AuthorizeService authorizeService;
    @Autowired
    private RedisService redisService;
    @Autowired

    private SmsUtil smsUtil;
    @Autowired
    private JiGuangUtil jiGuangUtil;

    @Autowired
    private RsaEncrypt rsaEncrypt;
    @Autowired
    private QcloudUtil qcloudUtil;

    @Value("${aliyun.sms.templateCode}")
    private String templateCode;

    @Value("${jiguang.password}")
    private String password;
    @Autowired
    private SensitiveFilter sensitiveFilter;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 手机登录，未注册的手机注册成为新用户，已注册的实现登录
     */
    @PostMapping("/open/register")
    public Result register(@RequestParam("mobile") String mobile, @RequestParam("verifyCode") String verifyCode) {
        if (StringUtils.isEmpty(mobile) || StringUtils.length(mobile) != 11) {
            return Result.fail(SystemErrorType.MOBILE_ERROR);
        }
        if (StringUtils.isEmpty(verifyCode) || StringUtils.length(verifyCode) != 4) {
            return Result.fail(SystemErrorType.VERIFY_CODE_LENGTH_ERROR);
        }
        String code = redisService.getRedisValue(mobile);
        if (!verifyCode.equals(code)) {
            return Result.fail(SystemErrorType.VERIFY_CODE_ERROR);
        }

        Result result = (Result<User>) userService.queryByMobile(mobile);
        log.info("登录结果返回:{}", result);
        if (result.isSuccess()) {
            if (result.getData() == null) {

                UserForm user = new UserForm();
                user.setOpenId("");
                user.setMobile(mobile);
                user.setUsername(mobile);
                user.setNickname(RandomUtil.getRandomNumString(4));
                user.setPassword(passwordEncoder().encode(mobile));
                result = userService.save(user);
                registerJiGuang(result);

                log.info("创建新用户:{}", result.isSuccess());
            }
            MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
            map.add("username", mobile);
            map.add("password", mobile);
            map.add("grant_type", "password");
            map.add("scope", "read");
            Object token = authorizeService.authorize(map);
            if (token != null) {
                LinkedHashMap r = (LinkedHashMap) token;
                User userinfo = (User) result.getData();
                r.put("id", userinfo.getId());
                return Result.success(r);
            }
            return Result.fail();

        }
        return Result.fail();

    }


    /**
     * 手机登录，未注册的手机注册成为新用户，已注册的实现登录
     */
    @PostMapping("/open/login")
    public Result login(@RequestParam("mobile") String mobile, @RequestParam("password") String password) {
        if (StringUtils.isEmpty(mobile) || StringUtils.length(mobile) != 11) {
            return Result.fail(SystemErrorType.MOBILE_ERROR);
        }
        if (StringUtils.isEmpty(password) || StringUtils.length(password) <6) {
            return Result.fail(SystemErrorType.VERIFY_CODE_LENGTH_ERROR);
        }

        Result <User> result = (Result<User>) userService.queryByMobile(mobile);


        if (result.isSuccess()&&result.getData()!=null) {
            User user = result.getData();
            if(user!=null){
                log.info("password:{}",password);
                log.info("user saved password:{}",user.getPassword());
                boolean match = passwordEncoder().matches(password,user.getPassword());
                log.info("登录结果返回:{}", match);
                if(match){
                    MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
                    map.add("username", mobile);
                    map.add("password", mobile);
                    map.add("grant_type", "password");
                    map.add("scope", "read");
                    log.info("生产token中");
                    Object token = authorizeService.authorize(map);
                    if (token != null) {
                        LinkedHashMap r = (LinkedHashMap) token;
                        User userinfo = (User) result.getData();
                        r.put("id", userinfo.getId());
                        return Result.success(r);
                    }
                }else{
                    log.info("密码登录失败:{}", match);
                    return Result.fail("密码错误");
                }
            }else{
                return Result.fail("用户尚未注册，请先注册");
            }
        }
        return Result.fail();
    }

    private void registerJiGuang(Result result) {
        if (result.isSuccess()) {
            log.info("注册极光:{}", result);
            User userMap = (User) result.getData();
            RegisterInfo registerInfo = RegisterInfo.newBuilder()
                    .setUsername(userMap.getId())
                    .setPassword(password)
                    .setAvatar(userMap.getAvatar() == null ? userMap.getAvatar() : "")
                    .setGender(userMap.getSex())
                    .setNickname(userMap.getNickname())
                    .build();
            jiGuangUtil.registerUser(registerInfo);
        }
    }

    /**
     * 手机登录，未注册的手机注册成为新用户，已注册的实现登录
     */
    @PostMapping("/open/registerByJiGuang")
    public Result registerByJiGuang(@RequestParam("encryptMobile") String encryptMobile) {
        if (StringUtils.isEmpty(encryptMobile)) {
            return Result.fail("手机号码有误");
        }
        String mobile = rsaEncrypt.decrypt(encryptMobile);
        log.info("解密手机号:{}", mobile);
        if (StringUtils.isEmpty(mobile) || StringUtils.length(mobile) != 11) {
            return Result.fail(SystemErrorType.MOBILE_ERROR);
        }

        Result result = (Result<User>) userService.queryByMobile(mobile);
        log.info("登录结果返回:{}", result);
        if (result.isSuccess()) {
            if (result.getData() == null) {

                UserForm user = new UserForm();
                user.setOpenId("");
                user.setMobile(mobile);
                user.setUsername(mobile);
                user.setNickname(RandomUtil.getRandomNumString(4));
                user.setPassword(passwordEncoder().encode(mobile));
                result = userService.save(user);
                registerJiGuang(result);

                log.info("创建新用户:{}", result.isSuccess());
            }
            MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
            map.add("username", mobile);
            map.add("password", mobile);
            map.add("grant_type", "password");
            map.add("scope", "read");
            Object token = authorizeService.authorize(map);
            if (token != null) {
                LinkedHashMap r = (LinkedHashMap) token;
                User userinfo = (User) result.getData();
                r.put("id", userinfo.getId());
                return Result.success(r);
            }
            return Result.fail();

        }


        return Result.fail("登录失败，请重试");
    }

    /**
     * 获取验证码
     */
    @GetMapping("/open/getVerifyCode")
    public Result getVerifyCode(@RequestParam("mobile") String mobile) {
        String code = RandomUtil.getRandomNumString(4);
        redisService.setRedisValue(mobile, code);
        //发送短信

        String templateParam = "{\"code\":\"" + code + "\"}";
        try {
            smsUtil.sendSms(mobile, templateCode, templateParam);
        } catch (ClientException e) {
            e.printStackTrace();
            return Result.fail();
        }
        //todo 处理限流
        return Result.success();
    }

    /**
     * 人脸校验获取数字验证码
     */
    @GetMapping("/open/user/getLiveCode")
    public Result getLiveCode() {
        String code = qcloudUtil.getLiveCode();
        return Result.success(code);
    }

    /**
     * 人脸比对
     */
    @PostMapping("/user/livenessCompare")
    public Result livenessCompare(HttpServletRequest request, String imageUrl, String videoUrl, String code) {
        User user = getUser(request);
        if (StringUtils.isEmpty(imageUrl)) {
            return Result.fail("图片不能为空");
        }
        if (StringUtils.isEmpty(videoUrl)) {
            return Result.fail("视频不能为空");
        }
        if (StringUtils.isEmpty(code)) {
            return Result.fail("校验码不能为空");
        }
        log.info("imageUrl:{},videoUrl:{},code:{}", imageUrl, videoUrl, code);
        boolean result = qcloudUtil.livenessCompare(imageUrl, videoUrl, code);
        if (result) {
            MomentForm moment = new MomentForm();
            moment.setUserId(user.getId());
            moment.setTitle("我通过了真人认证，获得了'真人勋章'");
            moment.setUrls(imageUrl);
            moment.setType(2);
            momentService.save(moment);
            user.setRealMan(1);
            user.setVipStatus(VipStatus.yes.getCode());
            if (user.getDeadline() == null) {
                user.setDeadline(DateUtils.addDays(new Date(), 7));
            } else if (user.getDeadline().before(new Date())) {
                user.setDeadline(DateUtils.addDays(new Date(), 7));
            } else if (user.getDeadline().after(new Date())) {
                user.setDeadline(DateUtils.addDays(user.getDeadline(), 7));
            }
            userService.update(user);
        }
        return Result.success(result);
    }


    @PutMapping("/user")
    public Result update(HttpServletRequest request, @RequestBody UserRequestEntity userRequestEntity) {
        User existUser = getUser(request);
        if (StringUtils.isNotEmpty(userRequestEntity.getAvatar())) {
            existUser.setAvatar(userRequestEntity.getAvatar());
        }
        if (StringUtils.isNotEmpty(userRequestEntity.getCity())) {
            existUser.setCity(userRequestEntity.getCity());
        }
        if (StringUtils.isNotEmpty(userRequestEntity.getProvince())) {
            existUser.setProvince(userRequestEntity.getProvince());
        }
        if (StringUtils.isNotEmpty(userRequestEntity.getNickname())) {
            existUser.setNickname(userRequestEntity.getNickname());
        }
        if (userRequestEntity.getSex() != null) {
            existUser.setSex(userRequestEntity.getSex());
        }
        if (userRequestEntity.getBirthday() != null) {
            log.info("userRequestEntity.getBirthday:{}", userRequestEntity.getBirthday());
            existUser.setBirthday(userRequestEntity.getBirthday());
        }
        if (userRequestEntity.getCity() != null) {
            existUser.setCity(userRequestEntity.getCity());
        }
        if (userRequestEntity.getProvince() != null) {
            existUser.setProvince(userRequestEntity.getProvince());
        }
        if (userRequestEntity.getDescription() != null) {
            String filted = sensitiveFilter.filter(userRequestEntity.getDescription(), '*');
            existUser.setDescription(filted);
        }
        if (userRequestEntity.getHeight() != 0) {
            existUser.setHeight(userRequestEntity.getHeight());
        }
        if (userRequestEntity.getWeight() != 0) {
            existUser.setWeight(userRequestEntity.getWeight());
        }
        if (userRequestEntity.getWeixin() != null) {
            String filted = sensitiveFilter.filter(userRequestEntity.getWeixin(), '*');
            existUser.setWeixin(filted);
        }
        if (userRequestEntity.getJob() != null) {
            existUser.setJob(userRequestEntity.getJob());
        }
        if (userRequestEntity.getPurpose() != null) {
            String filted = sensitiveFilter.filter(userRequestEntity.getPurpose(), '*');
            existUser.setPurpose(filted);
        }
        if (userRequestEntity.getRegistrationId() != null && !userRequestEntity.getRegistrationId().equals(existUser.getRegistrationId())) {
            existUser.setRegistrationId(userRequestEntity.getRegistrationId());
        }
        jiGuangUtil.updateUserInfo(existUser.getId(), existUser.getNickname(), DateUtils.formatDate(existUser.getBirthday()), existUser.getDescription(), existUser.getSex(), existUser.getCity(), existUser.getAddress(), existUser.getAvatar());
        userService.update(existUser);
        return Result.success(existUser);
    }

    @GetMapping("/user/getUserInfo")
    public Result getUserInfo(HttpServletRequest request) {
        User user = getUser(request);
        return userService.queryByMobile(user.getMobile());
    }

    @GetMapping("/open/user/{id}")
    public Result getUserInfoById(@PathVariable(value = "id") String id) {

        return userService.getById(id);
    }

    @GetMapping("open/user/queryByMobile")
    public Result queryByMobile(String mobile) {
        return userService.queryByMobile(mobile);
    }

    /**
     * 查询喜欢我的和我喜欢的人数
     */
    @GetMapping("/user/statistics")
    public Result getStatistics(HttpServletRequest request) {
        User user = getUser(request);
        return userService.getStatistics(user.getId());
    }

    @GetMapping("/user/getMyFollow")
    public Result getMyFollow(HttpServletRequest request, Integer pageSize, Integer pageNo) {
        User user = getUser(request);
        UserQueryForm queryForm = new UserQueryForm();
        queryForm.setCurrent(pageNo);
        queryForm.setSize(pageSize);
        queryForm.setUserId(user.getId());
        return userService.getMyFollow(queryForm);
    }

    @GetMapping("/user/getFollowMe")
    public Result getFollowMe(HttpServletRequest request, Integer pageSize, Integer pageNo) {
        User user = getUser(request);
        UserQueryForm queryForm = new UserQueryForm();
        queryForm.setCurrent(pageNo);
        queryForm.setSize(pageSize);
        queryForm.setFollowUserId(user.getId());
        return userService.getFollowMe(queryForm);
    }

    @GetMapping("/open/user/recommend")
    public Result recommend(String userId, Integer pageSize, Integer pageNo) {
        UserQueryForm queryForm = new UserQueryForm();
        queryForm.setCurrent(pageNo);
        queryForm.setSize(pageSize);
        queryForm.setUserId(userId);
        return userService.recommend(queryForm);
    }

    @GetMapping("/user/nearBy")
    public Result nearBy(HttpServletRequest request, Integer pageSize, Integer pageNo) {
        User user = getUser(request);
        Result<UserPosition> result = userPositionService.query(user.getId());
        if (result.isSuccess()) {
            UserPosition userPosition = result.getData();
            double latitude = userPosition.getLatitude();
            double longitude = userPosition.getLongitude();

            UserQueryForm queryForm = new UserQueryForm();

            queryForm.setLongitude(longitude);
            queryForm.setLatitude(latitude);

            queryForm.setCurrent(pageNo);
            queryForm.setSize(pageSize);
            queryForm.setUserId(user.getId());
            return userService.nearBy(queryForm);
        }
        return Result.fail();

    }

    public JSONObject parseMobile(UserRequestEntity userRequestEntity) {
        String encryptedData = userRequestEntity.getEncryptedData(), sessionKey, iv = userRequestEntity.getIv();
        HttpUriRequest request = RequestBuilder.get("https://api.weixin.qq.com/sns/jscode2session")
                .addParameter("appid", WeixinConstant.WEIXIN_MINA_APP_ID).addParameter("secret", WeixinConstant.WEIXIN_MINA_SECRET)
                .addParameter("grant_type", "authorization_code").addParameter("js_code", userRequestEntity.getCode()).build();

        log.debug("Third weixin mina check request url is: {}", request.getURI().toString());

        String errorCodeKey = "errcode";
        String errorMsgKey = "errmsg";
        String exceptionMessagePrefix = "获取微信access_token失败";
        JSONObject jsonObject = this.doRequestAndCheckResponse(request, errorCodeKey, errorMsgKey, exceptionMessagePrefix);

        // 这里需要unionId,如果打印出来的是openid的话，需要按照微信给的文档到公众平台进行相关的操作设置
        log.debug("Third weixin mina unionId and session_key json is : {}", jsonObject.toJSONString());


        return jsonObject;
    }

    public JSONObject decryptUserInfo(String encryptedData, String sessionKey, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.decodeBase64(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decodeBase64(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decodeBase64(iv);

        try {
            // 如果密钥不足16位，那么就补足. 这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }

            // JDK自带AES不支持PKCS7Padding，这里要用Bouncy提供的实现
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return JSONObject.parseObject(result);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        return null;
    }

    private JSONObject doRequestAndCheckResponse(HttpUriRequest request, String errorCodeKey, String errorMsgKey,
                                                 String exceptionMessagePrefix) {
        try (CloseableHttpResponse response = HttpClientBuilder.create().build().execute(request)) {
            return this.checkResponse(JSON.parseObject(EntityUtils.toString(response.getEntity())), errorCodeKey,
                    errorMsgKey, exceptionMessagePrefix);
        } catch (Exception e) {
            return null;
        }
    }

    private JSONObject checkResponse(JSONObject jsonObject, String errorCodeKey, String errorMsgKey,
                                     String exceptionMessagePrefix) {
        Integer errorCode = jsonObject.getInteger(errorCodeKey);
        if (errorCode != null && errorCode.intValue() > 0) {
            return null;
        }

        return jsonObject;
    }
}
